﻿using TribesPSO;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;


namespace TribesPSOTest.NUnit
{


    /// <summary>
    ///This is a test class for TribeTest and is intended
    ///to contain all TribeTest Unit Tests
    ///</summary>
    [TestFixture()]
    public class TribeTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        /// <summary>
        ///A test for TryRemoveWorstParticle
        ///</summary>
        [Test()]
        public void TryRemoveWorstParticleTest()
        {
            System.Random rng = new System.Random();
            IObjectiveFunction function = new Rosenbrock();

            List<Particle> particleList = new List<Particle>();

            //Generate 10 random particles inside the search space
            for (int n = 0; n < 10; n++)
            {
                EuclidianVectorBuilder positionBuilder = new EuclidianVectorBuilder();
                for(int dimension = 0; dimension<function.Dimensions;dimension++)
                {
                    double range = function.MaxBounds[dimension] - function.MinBounds[dimension];
                    double min = function.MinBounds[dimension];
                    positionBuilder.AddDimensionData(rng.NextDouble() * range + min);
                }
                particleList.Add(new IndependentGaussianParticle(function, positionBuilder.ToEuclidianVector()));
            }
            Particle worstParticle = particleList.OrderBy(o => o.CurrentError).Last();

            Tribe target = new Tribe(particleList);
            bool expected = true;
            bool actual;

            Assert.IsTrue(target.TribeMembers.Contains(worstParticle));
            actual = target.TryRemoveWorstParticle();
            Assert.AreEqual(expected, actual);

            Assert.IsFalse(target.TribeMembers.Contains(worstParticle));
        }

        /// <summary>
        /// Try to remove the last particle from a tribe when it has no better informers
        /// </summary>
        [Test()]
        public void TryRemoveWorstMonoParticleTest()
        {
            System.Random rng = new System.Random();
            IObjectiveFunction function = new Rosenbrock();

            EuclidianVectorBuilder positionBuilder = new EuclidianVectorBuilder();
            for (int dimension = 0; dimension < function.Dimensions; dimension++)
            {
                double range = function.MaxBounds[dimension] - function.MinBounds[dimension];
                double min = function.MinBounds[dimension];
                positionBuilder.AddDimensionData(rng.NextDouble() * range + min);
            }

            Particle candidate = new HypersphereParticle(function, positionBuilder.ToEuclidianVector());
            Tribe target = new Tribe(candidate);
            bool expected = false;
            bool actual;

            Assert.IsTrue(target.TribeMembers.Contains(candidate));
            actual = target.TryRemoveWorstParticle();
            Assert.AreEqual(expected, actual);
            Assert.IsTrue(target.TribeMembers.Contains(candidate));
        }

        /// <summary>
        /// Try to remove the last particle from a tribe when it has a better informer
        /// </summary>
        [Test()]
        public void TryRemoveWorstMonoParticleTest2()
        {
            System.Random rng = new System.Random();
            IObjectiveFunction function = new Rosenbrock();

            List<Tribe> tribeList = new List<Tribe>();

            for (int n = 0; n < 2; n++)
            {
                EuclidianVectorBuilder positionBuilder = new EuclidianVectorBuilder();
                for (int dimension = 0; dimension < function.Dimensions; dimension++)
                {
                    double range = function.MaxBounds[dimension] - function.MinBounds[dimension];
                    double min = function.MinBounds[dimension];
                    positionBuilder.AddDimensionData(rng.NextDouble() * range + min);
                }
                Particle candidate = new HypersphereParticle(function, positionBuilder.ToEuclidianVector());
                tribeList.Add(new Tribe(candidate));
            }

            Tribe betterTribe = tribeList.OrderBy(o => o.BestSolution.Error).First();
            Tribe worseTribe = tribeList.OrderBy(o => o.BestSolution.Error).Last();

            //Link up the tribes
            betterTribe.AddInformer(worseTribe);
            
            bool expected = false;
            bool actual = betterTribe.TryRemoveWorstParticle();

            Assert.AreEqual(expected, actual);

            expected = true;
            actual = worseTribe.TryRemoveWorstParticle();

            Assert.AreEqual(expected, actual);
        }

        [Test(), ExpectedException(typeof(ArgumentNullException))]
        public void NullMembersCtorTest()
        {
            Tribe target = new Tribe((Particle)null);
        }

        [Test(), ExpectedException(typeof(ArgumentNullException))]
        public void NullMembersCollectionCtorTest()
        {
            Tribe target = new Tribe((IEnumerable<Particle>)null);
        }

        [Test(), ExpectedException(typeof(ArgumentException))]
        public void EmptyMembersCtorTest()
        {
            Tribe target = new Tribe(Enumerable.Empty<Particle>());
        }

        [Test(), ExpectedException(typeof(ArgumentNullException))]
        public void NullRngCtorTest()
        {
            Tribe target = new Tribe(new HypersphereParticle(new Rosenbrock(), new EuclidianVector(0, 0)),
                                        null);
        }
    }
}
